using System;
using Com.ContentRendering.Api.ParserEngine.Exceptions;
using System.Collections.Generic;

namespace Com.ContentRendering.Api.ParserEngine.State
{
    /// <summary/>
    internal abstract class AbstractState
    {
        protected DocumentParser _documentParser;
        protected OutputNodeInterface _outputNode;
        private int _currentLine;
        private int _currentColumn;
        private Queue<char> _debugBuffer = new Queue<char>();

        /// <summary/>
        internal AbstractState(AbstractState state)
        {
            this._outputNode = state._outputNode;
            this._documentParser = state._documentParser;
            this._currentLine = state._currentLine;
            this._currentColumn = state._currentColumn;
            this._debugBuffer = state._debugBuffer;
        }

        /// <summary/>
        internal AbstractState(
            DocumentParser documentParser,
            OutputNodeInterface outputNode)
        {
            this._outputNode = outputNode;
            this._documentParser = documentParser;
            this._currentLine = Constant.DEFAULT_LINE;
            this._currentColumn = Constant.DEFAULT_COLUMN;
            this._debugBuffer = new Queue<char>();
        }

        /// <summary/>
        protected abstract void ParseEscapeCharacter();

        /// <summary/>
        protected abstract void ParseStartCharacter();

        /// <summary/>
        protected abstract void ParseEndCharacter();

        /// <summary/>
        protected abstract void ParseTransparentCharacter();

        /// <summary/>
        protected abstract void ParseIfCharacter();

        /// <summary/>
        protected abstract void ParseIfNotCharacter();

        /// <summary/>
        protected abstract void ParseSubstitutionCharacter();

        /// <summary/>
        protected abstract void ParseChildCharacter();

        /// <summary/>
        protected abstract void ParseParentCharacter();

        /// <summary/>
        protected abstract void ParseOtherCharacter(char value);

        /// <summary/>
        internal void Parse(char value)
        {
            // is the queue full
            if (this._debugBuffer.Count == Constant.DEBUG_BUFFER_MAX_COUNT)
            {
                this._debugBuffer.Dequeue();
            }

            // if the char is a line feed then change the position
            if (value == Constant.CHAR_LINE_FEED)
            {
                this._currentLine++;
                this._currentColumn = Constant.DEFAULT_COLUMN;
            }
            else
            {
                // shift the column
                this._currentColumn++;
            }

            try
            {
                // process the input
                this.CharHandler(value);
            }
            catch (Exception exception)
            {
                // wrap the exception with the more detailed exception
                throw new ParserException(
                    exception,
                    value, 
                    this._currentLine, 
                    this._currentColumn,
                    new string(this._debugBuffer.ToArray()));
            }

            // add the new items
            this._debugBuffer.Enqueue(value);
        }

        /// <summary/>
        private void CharHandler(char value)
        {
            // determine which tag is being parsed
            if (value == this._documentParser.SpecialCharacters.Substitution)
            {
                this.ParseSubstitutionCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.Escape)
            {
                this.ParseEscapeCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.If)
            {
                this.ParseIfCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.IfNot)
            {
                this.ParseIfNotCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.Start)
            {
                this.ParseStartCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.Child)
            {
                this.ParseChildCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.End)
            {
                this.ParseEndCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.Transparent)
            {
                this.ParseTransparentCharacter();
            }
            else if (value == this._documentParser.SpecialCharacters.Parent)
            {
                this.ParseParentCharacter();
            }
            else 
            {
                this.ParseOtherCharacter(value);
            }
        }

        /// <summary/>
        protected abstract void EndHandler();

        /// <summary/>
        internal void End()
        {
            // if the nested level is greater than zero a tag wasn't closed
            if (this._outputNode.Parent != null)
            {
                throw new MissingEndCharacterException();
            }

            // run concrete state specific checks
            this.EndHandler();
        }
    }
}
